﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.IO;
using System.Diagnostics;

namespace DDFileSyncPlugin
{
    public class CloudDriveManager
    {
        CloudStorageAccount _account;
        CloudDrive _cloudDrive = null;

        public CloudDrive CloudDrive
        {
            get { return _cloudDrive; }
            set { _cloudDrive = value; }
        }
        string _vhdName;

        public string VhdName
        {
            get { return _vhdName; }
            set { _vhdName = value; }
        }
        char _driveLetter;

        public char DriveLetter
        {
            get { return _driveLetter; }
            set { _driveLetter = value; }
        }
        int _cacheSize;

        public int CacheSize
        {
            get { return _cacheSize; }
            set { _cacheSize = value; }
        }
        LocalResource _cache;

        public LocalResource Cache
        {
            get { return _cache; }
            set { _cache = value; }
        }
        string _cachePartitionName;

        public string CachePartitionName
        {
            get { return _cachePartitionName; }
            set { _cachePartitionName = value; }
        }

        public int DriveSizeInMB { get; set; }
       // const int DISK_SIZE = 20;
        const int DISK_SIZE = 1048576;
       // const int MAX_DISK_SIZE = 1048576;
        public CloudDriveManager(CloudStorageAccount account, string vhdPathAndName, char driveLetter, LocalResource cache, int cacheSize, string driveCachePartition)
        {
            _account = account;
            _vhdName = vhdPathAndName;
            _driveLetter = driveLetter;
           // _cacheSize = cache.MaximumSizeInMegabytes / 2;
            _cacheSize = cacheSize;
            _cache = cache;
            _cachePartitionName = driveCachePartition;
        }

        public bool Mount()
        {
            Trace.TraceInformation(string.Format("mounting drive {0}", _vhdName));
            _cloudDrive = _account.CreateCloudDrive(_vhdName);

            var driveLetter = _cloudDrive.Mount(_cacheSize, DriveMountOptions.Force);
            Trace.TraceInformation(string.Format("mounted drive letter {0}", driveLetter));

            return VerifyDriveLetter();
        }


        public bool VerifyDriveLetter()
        {
            Trace.TraceInformation("verifying drive letter");
            bool rv = false;
            if (RoleEnvironment.IsEmulated)
            {
                Trace.TraceInformation("Can't change drive letter in emulator");
                //return false;
            }

            try
            {
                DriveInfo d = new DriveInfo(_cloudDrive.LocalPath);
                if (string.IsNullOrEmpty(_cloudDrive.LocalPath))
                {
                    Trace.TraceInformation("verifydriveLetter: Not Mounted?");
                    throw new InvalidOperationException("drive is not mounted");
                }

                if (!char.IsLetter(_cloudDrive.LocalPath[0]))
                {
                    Trace.TraceInformation("verifiydriveLeter: Not a letter?");
                    throw new InvalidOperationException("verifydriveletter - not a letter?");
                }

                if (IsSameDrive())
                {
                    Trace.TraceInformation("is same drive; no need to diskpart...");
                    return true;
                }

                char mountedDriveLetter = CurrentLocalDrive(_vhdName);
                RunDiskPart(_driveLetter, mountedDriveLetter);

                if (!IsSameDrive())
                {
                    var msg = "Drive change failed to change";
                    Trace.TraceError(msg);
                    throw new ApplicationException(msg);
                }
                else
                {
                    Mount();
                }

                Trace.TraceInformation("verifydriveletter done!!");
                return rv;

            }
            catch (Exception ex)
            {
                Trace.TraceInformation("error verifydriveletter "+ ex.Message);
                return rv;
            }

        }

        bool IsSameDrive()
        {
            char targetDrive = _driveLetter.ToString().ToLower()[0];
            char currentDrive = CurrentLocalDrive(_vhdName);

            string msg = string.Format(
                "target drive: {0} - current drive: {1}",
                targetDrive,
                currentDrive);

            Trace.TraceInformation(msg);

            if (targetDrive == currentDrive)
            {
                Trace.TraceInformation("verifydriveLetter: already same drive");
                return true;
            }
            else
                return false;

        }

        char CurrentLocalDrive(string vhdName)
        {
            KeyValuePair<string, Uri> currentDrivePath = new KeyValuePair<string, Uri>();
            foreach (var drive in CloudDrive.GetMountedDrives())
            {
                if (drive.Value.AbsolutePath.Contains(_vhdName))
                {
                    currentDrivePath = drive;
                    break;
                }
               // throw new ApplicationException("drive can't be found in listed mounted drives " + _vhdName);
            }

            if (!string.IsNullOrEmpty(currentDrivePath.Key))
            {
                char currentDrive = currentDrivePath.Key.ToLower()[0];
                return currentDrive;
            }
            else
            {
                throw new ApplicationException("drive can't be found in listed mounted drives " + _vhdName);

            }
        }

        void RunDiskPart(char destinationDriveLetter, char mountedDriveLetter)
        {
            string cacheRootPath = null;
            if (!string.IsNullOrEmpty(_cachePartitionName))
            {
                cacheRootPath = Path.Combine(_cache.RootPath, _cachePartitionName);
                if (!Directory.Exists(cacheRootPath))
                {
                    Directory.CreateDirectory(cacheRootPath);


                }

            }
            else
            {

                cacheRootPath = _cache.RootPath;
            }
            string diskpartFile = Path.Combine(cacheRootPath, "diskpart.txt");

            if (File.Exists(diskpartFile))
            {
                File.Delete(diskpartFile);
            }

            string cmd = "select volume = " + mountedDriveLetter + "\r\n" + "assign letter = " + destinationDriveLetter;
            File.WriteAllText(diskpartFile, cmd);

            //start the process
            Trace.TraceInformation("running diskpart now!!!!");
            Trace.TraceInformation("using " + cmd);
            using (Process changeletter = new Process())
            {
                changeletter.StartInfo.Arguments = "/s" + " " + diskpartFile;
                changeletter.StartInfo.FileName = System.Environment.GetEnvironmentVariable("WINDIR") + "\\System32\\diskpart.exe";
                //#if !DEBUG
                changeletter.Start();
                changeletter.WaitForExit();
                //#endif
            }

            File.Delete(diskpartFile);

        }

        public void CreateDrive()
        {
            try
            {
                CloudBlobClient blobClient = _account.CreateCloudBlobClient();
                

                CloudPageBlob pageBlob = blobClient

                    .GetPageBlobReference(_vhdName);
                Trace.TraceInformation("Page Blob Reference " + pageBlob.Uri.ToString());
                pageBlob.Container.CreateIfNotExist();

               // _cloudDrive = _account.CreateCloudDrive(_vhdName);

                _cloudDrive = _account.CreateCloudDrive(pageBlob.Uri.ToString());
                Trace.TraceInformation("using account " + _account.BlobEndpoint + " to create " + pageBlob.Uri.ToString());
              //  var rv = _cloudDrive.CreateIfNotExist(DISK_SIZE);
                var rv = _cloudDrive.CreateIfNotExist(DISK_SIZE);
                Trace.TraceInformation("done create drive");
            }
            catch (Exception ex)
            {
                Trace.TraceInformation("error on CreateDrive "+  ex.Message);
            }
        }



    }
}
